#include "ConfigEdit.h"
#include "./ui_ConfigEdit.h"
#include <QFileDialog>
#include "../config_client/XConfigClient.h"
#include "xplatform/XTools.h"
#include <QSpinBox>
#include <float.h>
#include <QComboBox>
#include <QMessageBox>
#include <thread>
using namespace google;
using namespace protobuf;
using namespace std::chrono_literals;
ConfigEdit::ConfigEdit(QWidget *parent)
    : ui(new Ui::ConfigEdit)
{
    ui->setupUi(this);
    setWindowTitle("添加配置");
    connect(ui->btnSave, &QPushButton::clicked, this, &ConfigEdit::onBtnSaveClick);
    connect(ui->btnLoadProto, &QPushButton::clicked, this, &ConfigEdit::LoadProto);
    config_row_count_ = ui->formLayout->rowCount();
}

bool ConfigEdit::LoadConfig(const char *ip, int port)
{
    XConfigClient::Get()->LoadConfig(ip, port);
    if (!config_)
    {
        config_ = new xmsg::XConfig();
    }
    bool is_ok = false;
    for (int i = 0; i < 100; ++i)
    {
        is_ok = XConfigClient::Get()->GetConfig(ip, port, config_);
        if (is_ok)
            break;
        std::this_thread::sleep_for(10ms);
    }
    if (!is_ok || !config_ || config_->proto().empty())
    {
        XCOM_LOG_DEBUG("配置数据失败");
        return false;
    }
    QString proto_filepath = "tmp.proto";
    QFile file(proto_filepath);
    if (!file.open(QFile::WriteOnly))
    {
        XCOM_LOG_DEBUG("配置数据失败");
        return false;
    }
    file.write(config_->proto().c_str());
    file.close();
    // if (message_)
    // {
    //     delete message_;
    //     message_ = nullptr;
    // }

    std::string s;
    message_ = XConfigClient::Get()->LoadPoto(proto_filepath.toStdString(), "", s);
    if (!message_)
    {
        AddLog("XConfigClient::Get()->LoadPoto failed!");
        return false;
    }
    ui->lineEditProtoName->setText(message_->GetTypeName().c_str());
    ui->plainTextEdit->setPlainText(QString::fromStdString(s));
    ui->serviceNameLineEdit->setText(config_->service_name().c_str());
    ui->serviceIpLineEdit->setText(config_->service_ip().c_str());
    ui->spinBoxPort->setValue(config_->service_port());
    bool ret = message_->ParseFromString(config_->private_pb());
    if(!ret){
        AddLog("ParseFromString failed!");
        return false;
    }
    // 根据message动态生成ui
    InitUi();
    XCOM_LOG_DEBUG("配置数据成功");
    return true;
}

void ConfigEdit::LoadProto()
{
    int cnt = ui->formLayout->rowCount();
    for (int i = config_row_count_; i < cnt; ++i)
    {
        ui->formLayout->removeRow(config_row_count_);
    }
    // if (message_)
    // {
    //     delete message_;
    //     message_ = nullptr;
    // }
    std::string class_name_str = ui->lineEditProtoName->text().toStdString();
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开proto文件"), "", tr("proto文件 (*.proto)"));
    if (fileName.isEmpty())
    {
        AddLog("文件不能为空");
        return;
    }
    std::string s;
    message_ = XConfigClient::Get()->LoadPoto(fileName.toStdString(), class_name_str, s);
    if (!message_)
    {
        AddLog("XConfigClient::Get()->LoadPoto failed!");
        return;
    }
    ui->lineEditProtoName->setText(message_->GetTypeName().c_str());
    ui->plainTextEdit->setPlainText(QString::fromStdString(s));
    // 根据message动态生成ui
    InitUi();
    // auto desc = message_->GetDescriptor();
    // int failed_count = desc->field_count();
    // auto int_box = new QDoubleSpinBox(this);
    // int_box->setDecimals(0);
    // for (int i = 0; i < failed_count; ++i)
    // {
    //     auto field = desc->field(i);
    //     // 支持数据类型：数子(整形)、字符串、bool、枚举、数组、结构体、字符串数组、结构体数组
    //     switch (field->type())
    //     {
    //     case FieldDescriptor::TYPE_DOUBLE:
    //     case FieldDescriptor::TYPE_FLOAT:
    //     {
    //         auto double_box = new QDoubleSpinBox(this);
    //         double_box->setRange(FLT_MIN, FLT_MAX);
    //         ui->formLayout->addRow(QString::fromStdString(field->name()), double_box);
    //         break;
    //     }
    //     // int64
    //     case FieldDescriptor::TYPE_SFIXED64:
    //     case FieldDescriptor::TYPE_SINT64:
    //     case FieldDescriptor::TYPE_INT64:
    //     {
    //         int_box->setMaximum(INT64_MIN);
    //         int_box->setMaximum(double(INT64_MAX));
    //         ui->formLayout->addRow(QString::fromStdString(field->name()), int_box);
    //         break;
    //     }
    //     // int32
    //     case FieldDescriptor::TYPE_SFIXED32:
    //     case FieldDescriptor::TYPE_SINT32: // int32, ZigZag-encoded varint on the wire
    //     case FieldDescriptor::TYPE_INT32:
    //     {
    //         int_box->setMaximum(INT32_MIN);
    //         int_box->setMaximum(INT32_MAX);
    //         ui->formLayout->addRow(QString::fromStdString(field->name()), int_box);
    //         break;
    //     }
    //     // uint64
    //     case FieldDescriptor::TYPE_UINT64:
    //     case FieldDescriptor::TYPE_FIXED64: // uint64, exactly eight bytes on the wire.
    //     {
    //         int_box->setMaximum(0);
    //         int_box->setMaximum(double(UINT64_MAX));
    //         ui->formLayout->addRow(QString::fromStdString(field->name()), int_box);
    //         break;
    //     }
    //     // uint32
    //     case FieldDescriptor::TYPE_FIXED32: // uint32, exactly four bytes on the wire.
    //     case FieldDescriptor::TYPE_UINT32:
    //     {
    //         int_box->setMaximum(0);
    //         int_box->setMaximum(UINT32_MAX);
    //         ui->formLayout->addRow(QString::fromStdString(field->name()), int_box);
    //         break;
    //     }
    //     case FieldDescriptor::TYPE_BOOL:
    //     {
    //         auto c = new QComboBox(this);
    //         c->addItem("true", true);
    //         c->addItem("false", false);
    //         ui->formLayout->addRow(QString::fromStdString(field->name()), c);
    //         break; // bool, varint on the wire.
    //     }
    //     case FieldDescriptor::TYPE_BYTES: // break; // Arbitrary byte array.
    //     case FieldDescriptor::TYPE_STRING:
    //         ui->formLayout->addRow(QString::fromStdString(field->name()), new QLineEdit(this));
    //         break; // UTF-8 text.
    //     case FieldDescriptor::TYPE_GROUP:
    //         break; // Tag-delimited message.  Deprecated.
    //     case FieldDescriptor::TYPE_MESSAGE:
    //         break; // Length-delimited message.
    //     case FieldDescriptor::TYPE_ENUM:
    //     {
    //         auto c = new QComboBox(this);
    //         for (int i = 0; i < field->enum_type()->value_count(); i++)
    //             c->addItem(QString::fromStdString(field->enum_type()->value(i)->name()), field->enum_type()->value(i)->number());
    //         ui->formLayout->addRow(QString::fromStdString(field->name()), c);
    //         break; // Enum, varint on the wire
    //     }
    //     default:
    //         break;
    //     }
    // }
}

void ConfigEdit::InitUi()
{
    int cnt = ui->formLayout->rowCount();
    for (int i = config_row_count_; i < cnt; ++i)
    {
        ui->formLayout->removeRow(config_row_count_);
    }
    // 根据message动态生成ui
    auto desc = message_->GetDescriptor();
    int failed_count = desc->field_count();
    auto int_box = new QDoubleSpinBox(this);
    int_box->setDecimals(0);
    auto reflection = message_->GetReflection();
    for (int i = 0; i < failed_count; ++i)
    {
        auto field = desc->field(i);
        // 支持数据类型：数子(整形)、字符串、bool、枚举、数组、结构体、字符串数组、结构体数组
        switch (field->type())
        {
        case FieldDescriptor::TYPE_DOUBLE:
        case FieldDescriptor::TYPE_FLOAT:
        {
            auto double_box = new QDoubleSpinBox(this);
            double_box->setRange(FLT_MIN, FLT_MAX);
            ui->formLayout->addRow(QString::fromStdString(field->name()), double_box);
            double_box->setValue(reflection->GetDouble(*message_, field));
            break;
        }
        // int64
        case FieldDescriptor::TYPE_SFIXED64:
        case FieldDescriptor::TYPE_SINT64:
        case FieldDescriptor::TYPE_INT64:
        {
            int_box->setMaximum(INT64_MIN);
            int_box->setMaximum(double(INT64_MAX));
            ui->formLayout->addRow(QString::fromStdString(field->name()), int_box);
            int_box->setValue(reflection->GetInt64(*message_, field));
            break;
        }
        // int32
        case FieldDescriptor::TYPE_SFIXED32:
        case FieldDescriptor::TYPE_SINT32: // int32, ZigZag-encoded varint on the wire
        case FieldDescriptor::TYPE_INT32:
        {
            int_box->setMaximum(INT32_MIN);
            int_box->setMaximum(INT32_MAX);
            ui->formLayout->addRow(QString::fromStdString(field->name()), int_box);
            int_box->setValue(reflection->GetInt32(*message_, field));
            break;
        }
        // uint64
        case FieldDescriptor::TYPE_UINT64:
        case FieldDescriptor::TYPE_FIXED64: // uint64, exactly eight bytes on the wire.
        {
            int_box->setMaximum(0);
            int_box->setMaximum(double(UINT64_MAX));
            ui->formLayout->addRow(QString::fromStdString(field->name()), int_box);
            int_box->setValue(reflection->GetUInt64(*message_, field));
            break;
        }
        // uint32
        case FieldDescriptor::TYPE_FIXED32: // uint32, exactly four bytes on the wire.
        case FieldDescriptor::TYPE_UINT32:
        {
            int_box->setMaximum(0);
            int_box->setMaximum(UINT32_MAX);
            ui->formLayout->addRow(QString::fromStdString(field->name()), int_box);
            int_box->setValue(reflection->GetUInt32(*message_, field));
            break;
        }
        case FieldDescriptor::TYPE_BOOL:
        {
            auto c = new QComboBox(this);
            c->addItem("true", true);
            c->addItem("false", false);
            ui->formLayout->addRow(QString::fromStdString(field->name()), c);
            c->setCurrentIndex(reflection->GetBool(*message_, field) ? 0 : 1);
            break; // bool, varint on the wire.
        }
        case FieldDescriptor::TYPE_BYTES: // break; // Arbitrary byte array.
        case FieldDescriptor::TYPE_STRING:{
            auto lineEdit = new QLineEdit(this);
            ui->formLayout->addRow(QString::fromStdString(field->name()), lineEdit);
            lineEdit->setText(QString::fromStdString(reflection->GetString(*message_, field)));
            break; // UTF-8 text.
        }
        case FieldDescriptor::TYPE_GROUP:
            break; // Tag-delimited message.  Deprecated.
        case FieldDescriptor::TYPE_MESSAGE:
            break; // Length-delimited message.
        case FieldDescriptor::TYPE_ENUM:
        {
            auto c = new QComboBox(this);
            for (int i = 0; i < field->enum_type()->value_count(); i++)
                c->addItem(QString::fromStdString(field->enum_type()->value(i)->name()), field->enum_type()->value(i)->number());
            ui->formLayout->addRow(QString::fromStdString(field->name()), c);
            c->setCurrentIndex(reflection->GetEnum(*message_, field)->number());
            break; // Enum, varint on the wire
        }
        default:
            break;
        }
    }
}

void ConfigEdit::onBtnSaveClick()
{
    if (!message_)
    {
        QMessageBox::information(this, "提示", "message_==nullptr,需要加载proto文件");
        accept();
        return;
    }
    if (ui->serviceNameLineEdit->text().isEmpty() || ui->serviceIpLineEdit->text().isEmpty() || ui->plainTextEdit->toPlainText().isEmpty() || ui->spinBoxPort->value() <= 0)
    {
        QMessageBox::information(this, "提示", "未设置基础配置");
        accept();
        return;
    }
    xmsg::XConfig config;
    // 基础信息
    config.set_service_name(ui->serviceNameLineEdit->text().toStdString());
    config.set_service_ip(ui->serviceIpLineEdit->text().toStdString());
    config.set_service_port(ui->spinBoxPort->value());
    config.set_proto(ui->plainTextEdit->toPlainText().toStdString());
    // 配置信息
    // 遍历输入
    auto descriptor = message_->GetDescriptor();
    auto reflection = message_->GetReflection();
    int cnt = ui->formLayout->rowCount();
    for (int i = config_row_count_; i < cnt; ++i)
    {
        // 找到key
        auto tem = ui->formLayout->itemAt(i, QFormLayout::LabelRole);
        QLabel *label = nullptr;
        if (tem)
        {
            label = qobject_cast<QLabel *>(tem->widget());
            if (!label)
                continue;
        }
        auto field_item = ui->formLayout->itemAt(i, QFormLayout::FieldRole)->widget();
        if (!field_item)
            continue;
        auto name = label->text();
        auto fieldDescriptor = descriptor->FindFieldByName(name.toStdString());
        if (!fieldDescriptor)
            continue;
        switch (fieldDescriptor->type())
        {
        case FieldDescriptor::TYPE_DOUBLE:
        case FieldDescriptor::TYPE_FLOAT:
        {
            auto double_box = qobject_cast<QDoubleSpinBox *>(field_item);
            auto v = double_box->value();
            reflection->SetFloat(message_, fieldDescriptor, v);
            break;
        }
        // int64
        case FieldDescriptor::TYPE_SFIXED64:
        case FieldDescriptor::TYPE_SINT64:
        case FieldDescriptor::TYPE_INT64:
        {
            auto int_box = qobject_cast<QDoubleSpinBox *>(field_item);
            auto v = int_box->value();
            reflection->SetInt64(message_, fieldDescriptor, v);
            break;
        }
        // int32
        case FieldDescriptor::TYPE_SFIXED32:
        case FieldDescriptor::TYPE_SINT32: // int32, ZigZag-encoded varint on the wire
        case FieldDescriptor::TYPE_INT32:
        {
            auto int_box = qobject_cast<QDoubleSpinBox *>(field_item);
            auto v = int_box->value();
            reflection->SetInt32(message_, fieldDescriptor, v);
            break;
        }
        // uint64
        case FieldDescriptor::TYPE_UINT64:
        case FieldDescriptor::TYPE_FIXED64:
        {
            auto int_box = qobject_cast<QDoubleSpinBox *>(field_item);
            auto v = int_box->value();
            reflection->SetUInt64(message_, fieldDescriptor, v);
            break;
        }
        // uint32
        case FieldDescriptor::TYPE_FIXED32: // uint32, exactly four bytes on the wire.
        case FieldDescriptor::TYPE_UINT32:
        {
            auto int_box = qobject_cast<QDoubleSpinBox *>(field_item);
            auto v = int_box->value();
            reflection->SetUInt32(message_, fieldDescriptor, v);
            break;
        }
        case FieldDescriptor::TYPE_BOOL:
        {
            auto c = qobject_cast<QComboBox *>(field_item);
            auto a = c->currentData().toBool();
            reflection->SetBool(message_, fieldDescriptor, a);
            break; // bool, varint on the wire.
        }
        case FieldDescriptor::TYPE_BYTES: // break; // Arbitrary byte array.
        case FieldDescriptor::TYPE_STRING:
        {
            auto c = qobject_cast<QLineEdit *>(field_item);
            auto s=c->text();
            reflection->SetString(message_, fieldDescriptor, s.toStdString());
            break; // UTF-8 text.
        }
        case FieldDescriptor::TYPE_GROUP:
            break; // Tag-delimited message.  Deprecated.
        case FieldDescriptor::TYPE_MESSAGE:
            break; // Length-delimited message.
        case FieldDescriptor::TYPE_ENUM:
        {
            auto c = qobject_cast<QComboBox *>(field_item);
            if (!c)
                continue;
            int a = c->currentData().toInt();
            reflection->SetEnumValue(message_, fieldDescriptor, a);
            break; // Enum, varint on the wire
        }
        default:
            break;
        }
    }
    std::string pb=message_->SerializeAsString();
    config.set_private_pb(pb);
    // 存储到配置中心
    XConfigClient::Get()->SaveConfig(&config);
    accept();
}
